package com.zy.swagger.service.sys.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.zy.swagger.constant.DingTalkResource;
import com.zy.swagger.dto.DingDeptDTO;
import com.zy.swagger.dto.DingTokenCache;
import com.zy.swagger.dto.DingUserDTO;
import com.zy.swagger.service.sys.DingTalkService;
import com.zy.swagger.util.ChineseCalendar;
import com.zy.swagger.util.DateUtil;
import com.zy.swagger.util.HttpRequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * description 钉钉接口服务实现类
 *
 * @author ZY
 * @date 2021年04月03日 20:50
 **/
@Slf4j
@Service
public class DingTalkServiceImpl implements DingTalkService {

    @Value("${dingTalk.appKey:}")
    private String dingTalkAppKey;

    @Value("${dingTalk.appSecret:}")
    private String dingTalkAppSecret;

    /**
     * 获取钉钉token
     *
     * @return 返回钉钉接口token
     */
    @Override
    public String getDingToken() {
        // token先从缓存中获取，如果获取不到，则调钉钉接口获取
        String s = DingTalkResource.tokenMap.get(DingTalkResource.TOKEN_KEY);
        if (StringUtils.isEmpty(s)) {
            return getToken();
        } else {
            DingTokenCache dingTokenCache = JSON.parseObject(s, DingTokenCache.class);
            if (dingTokenCache.getExpiresAt().getTime() <= System.currentTimeMillis()) {
                return getToken();
            }
            return dingTokenCache.getAccessToken();
        }
    }

    private String getToken() {
        String getTokenUrl = String.format(DingTalkResource.GET_DING_TOKEN_URL, dingTalkAppKey, dingTalkAppSecret);
        try {
            Map map = HttpRequestUtil.sendGet(getTokenUrl, null, Map.class);
            if (Objects.equals(map.get("errcode"), DingTalkResource.DD_OK)) {
                String accessToken = (String) map.get("access_token");
                // 将从钉钉接口获取的token存到本地缓存中
                DingTalkResource.tokenMap.put(DingTalkResource.TOKEN_KEY, JSON.toJSONString(DingTokenCache.builder().accessToken(accessToken).expiresAt(new Date(System.currentTimeMillis() + 7000 * 1000)).build()));
                return accessToken;
            } else {
                log.info("调用钉钉获取token接口，返回错误，提示信息：{}", map.get("errmsg"));
            }
        } catch (Exception e) {
            log.error("调用钉钉获取token接口异常：", e);
        }
        return null;
    }

    /**
     * 获取钉钉部门集合
     *
     * @return 返回钉钉所有部门集合
     */
    @Override
    public List<DingDeptDTO> getDingDeptList() {
        List<DingDeptDTO> retList = Lists.newArrayList();
        String getDingDeptUrl = String.format(DingTalkResource.GET_DING_DEPT_LIST_URL, getDingToken());
        try {
            Map map = HttpRequestUtil.sendGet(getDingDeptUrl, null, Map.class);
            int code = (int) map.get("errcode");
            if (Objects.equals(code, DingTalkResource.DD_OK)) {
                List list = JSON.parseObject(JSON.toJSONString(map.get("department")), List.class);
                if (!CollectionUtils.isEmpty(list)) {
                    list.forEach(x -> {
                        Map dept = JSON.parseObject(JSON.toJSONString(x), Map.class);
                        int id = (int) dept.get("id");
                        String deptName = (String) dept.get("name");
                        Integer parentId = dept.get("parentid") != null ? (int) dept.get("parentid") : null;
                        retList.add(DingDeptDTO.builder().id(id).deptName(deptName).parentId(parentId).build());
                    });
                }
            } else {
                log.info("调用钉钉获取所有部门的接口返回错误，提示信息：{}", map.get("errmsg"));
            }
        } catch (Exception e) {
            log.error("调用钉钉获取所有部门的接口异常：", e);
        }
        return retList;
    }

    /**
     * 根据部门ID获取其子部门集合
     *
     * @param deptId 部门ID
     * @param flag 是否递归查询所有子部门
     * @return 返回子部门集合
     */
    @Override
    public List<Integer> getDingSubDeptIdList(Integer deptId, Boolean flag) {
        Set<Integer> deptIdSet = Sets.newHashSet();
        String postUrl = String.format(DingTalkResource.GET_DING_SUB_DEPT_ID_LIST_URL, getDingToken());
        try {
            Map<String, Object> params = Maps.newHashMap();
            params.put("dept_id", deptId);
            Map map = HttpRequestUtil.sendPost(postUrl, null, params, Map.class);
            int code = (int) map.get("errcode");
            if (Objects.equals(code, DingTalkResource.DD_OK)) {
                Map result = JSON.parseObject(JSON.toJSONString(map.get("result")), Map.class);
                if (result == null || result.isEmpty()) {
                    return Lists.newArrayList(deptIdSet);
                }
                Set<Integer> subIdSet = JSON.parseObject(JSON.toJSONString(result.get("dept_id_list")), Set.class);
                if (CollectionUtils.isEmpty(subIdSet)) {
                    return Lists.newArrayList(deptIdSet);
                }
                deptIdSet.addAll(subIdSet);
                if (flag) {
                    for (int subId : subIdSet) {
                        deptIdSet.addAll(getDingSubDeptIdList(subId, true));
                    }
                    return Lists.newArrayList(deptIdSet);
                }
            } else {
                log.info("调用钉钉获取部门子部门ID集合的接口返回错误，提示信息：{}", map.get("errmsg"));
            }
        } catch (Exception e) {
            log.error("调用钉钉获取部门子部门ID集合的接口异常：", e);
        }
        return Lists.newArrayList(deptIdSet);
    }


    /**
     * 根据钉钉部门ID查询部门下面的人员
     *
     * @param deptId 部门ID
     * @param flag 是否递归查询所有子部门的人员
     * @return 返回部门下面人员集合
     */
    @Override
    public List<DingUserDTO> getDingUserList(Integer deptId, Boolean flag) {
        List<DingUserDTO> retList = Lists.newArrayList();
        String getDeptUserUrl = String.format(DingTalkResource.GET_DEPT_USERS_URL, getDingToken(), deptId);
        try {
            Map map = HttpRequestUtil.sendGet(getDeptUserUrl, null, Map.class);
            int code = (int) map.get("errcode");
            if (Objects.equals(code, DingTalkResource.DD_OK)) {
                List list = JSON.parseObject(JSON.toJSONString(map.get("userlist")), List.class);
                if (!CollectionUtils.isEmpty(list)) {
                    list.forEach(x -> retList.add(JSON.parseObject(JSON.toJSONString(x), DingUserDTO.class)));
                    if (flag) {
                        List<Integer> subIdList = this.getDingSubDeptIdList(deptId, true);
                        if (!CollectionUtils.isEmpty(subIdList)) {
                            for (int subId : subIdList) {
                                retList.addAll(getDingUserList(subId, true));
                            }
                        }
                    }
                }
            } else {
                log.info("调用钉钉获取部门下用户的接口返回错误，提示信息：{}", map.get("errmsg"));
            }
        } catch (Exception e) {
            log.error("调用钉钉获取部门下用户的接口异常：", e);
        }
        return retList;
    }

    /**
     * 查询某个部门某年某月的加班餐补明细
     *
     * @param deptName 部门名称
     * @param deptId   部门ID
     * @param year     年
     * @param month    月
     * @return 返回该部门加班补贴明细
     */
    @Override
    public Map<String, List<List<String>>> getOverTimeAttendList(String deptName, Integer deptId, Integer year, Integer month) throws Exception {
        Map<String, List<List<String>>> retMap = Maps.newHashMap();
        List<DingUserDTO> dingUserList = this.getDingUserList(deptId, true);
        // 对员工去重(方式1)
//        TreeSet<DingUserDTO> dingUserSet = Sets.newTreeSet(Comparator.comparing(DingUserDTO::getUserId));
//        dingUserSet.addAll(dingUserList);
//        dingUserList = Lists.newArrayList(dingUserSet);
        // 对员工去重(方式2)
        dingUserList = dingUserList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(DingUserDTO::getUserId))), ArrayList::new));

        String beginDateStr = DateUtil.getBeginDateStr(year, month);
        String endDateStr = DateUtil.getEndDateStr(year, month);
        List<String> dateList = DateUtil.getDateList(5, beginDateStr + "~" + endDateStr, 180);

        if (CollectionUtils.isEmpty(dingUserList)) {
            return retMap;
        }
        int deptTotalSupply = 0;
        for (DingUserDTO dto : dingUserList) {
            List<List<String>> empRows = Lists.newArrayList();
            String userId = dto.getUserId();
            String name = dto.getName();
            int totalSupply = 0;
            for (String date : dateList) {
                Date overTime1 = DateUtil.parseDate(date + " 20:00:00", DateUtil.NORMAL_PATTERN_2);
                Date overTime2 = DateUtil.parseDate(date + " 20:30:00", DateUtil.NORMAL_PATTERN_2);
                List<String> row = Lists.newArrayList();
                row.add(name);
                row.add(date);
                // 查询员工指定日期的打卡记录
                if (ChineseCalendar.NON_WORK_DAY.contains(date)) {
                    row.add("节假日");
                    row.add("餐补");
                    row.add("0");
                    empRows.add(row);
                    continue;
                }
                Map<String, Object> params = Maps.newHashMap();
                params.put("workDateFrom", date + " 00:00:00");
                params.put("workDateTo", date + " 23:59:59");
                // 必填，与offset和limit配合使用
                params.put("userIdList", Collections.singletonList(userId));
                // 必填，第一次传0，如果还有多余数据，下次传之前的offset加上limit的值
                params.put("offset", 0);
                // 必填，表示数据条数，最大不能超过50条
                params.put("limit", 50);
                String url = String.format(DingTalkResource.GET_DING_ATTENDANCE_URL, getDingToken());
                try {
                    Map map = HttpRequestUtil.sendPost(url, null, params, Map.class);
                    int code = (int) map.get("errcode");
                    if (Objects.equals(code, DingTalkResource.DD_OK)) {
                        String offDutyTime = "";
                        int mealSupply = 0;
                        List list = JSON.parseObject(JSON.toJSONString(map.get("recordresult")), List.class);
                        if (!CollectionUtils.isEmpty(list)) {
                            int flag = 0;
                            for (Object x : list) {
                                Map dayMap = JSON.parseObject(JSON.toJSONString(x), Map.class);
                                // 找到下班的那次打卡(最多1次)
                                if (Objects.equals(dayMap.get("checkType"), "OffDuty")) {
                                    flag++;
                                    Date userCheckTime = new Date((long) dayMap.get("userCheckTime"));
                                    offDutyTime = DateUtil.format(userCheckTime, DateUtil.SHORT_PATTERN_5);

                                    if (overTime1.getTime() <= userCheckTime.getTime() && userCheckTime.getTime() < overTime2.getTime()) {
                                        mealSupply = 20;
                                        totalSupply += mealSupply;
                                        row.add(offDutyTime);
                                        row.add("餐补");
                                        row.add(mealSupply + "");
                                        empRows.add(row);
                                    } else if (userCheckTime.getTime() >= overTime2.getTime()) {
                                        mealSupply = 25;
                                        totalSupply += mealSupply;
                                        row.add(offDutyTime);
                                        row.add("餐补");
                                        row.add(mealSupply + "");
                                        empRows.add(row);
                                    } else {
                                        row.add(offDutyTime);
                                        row.add("餐补");
                                        row.add(mealSupply + "");
                                        empRows.add(row);
                                    }
                                    break;
                                }
                            }
                            if (flag == 0) {
                                row.add("下班未打卡");
                                row.add("餐补");
                                row.add(mealSupply + "");
                                empRows.add(row);
                            }
                        } else {
                            row.add("无打卡记录");
                            row.add("餐补");
                            row.add(mealSupply + "");
                            empRows.add(row);
                        }
                    } else {
                        log.info("调用钉钉获取部门下用户的接口返回错误，提示信息：{}", map.get("errmsg"));
                    }
                } catch (Exception e) {
                    log.error("调用钉钉获取考勤接口异常：", e);
                }
            }
            deptTotalSupply += totalSupply;
            empRows.add(Arrays.asList(name, year + "年" + month + "月份", "", "餐补总计：", totalSupply + ""));
            retMap.put(name, empRows);
        }
        List<List<String>> deptTotal = Lists.newArrayList();
        deptTotal.add(Arrays.asList(deptName, year + "年" + month + "月份", "", "餐补总计：", deptTotalSupply + ""));

        Map<String, List<List<String>>> finalMap = Maps.newLinkedHashMap();
        finalMap.put(deptName + year + "年" + month + "月份-餐补总计", deptTotal);
        finalMap.putAll(retMap);
        return finalMap;
    }

}
